package com.tpsix.security;

import cn.hutool.core.util.StrUtil;
import com.tpsix.service.SysUserService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;

import javax.security.auth.login.AccountLockedException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@AllArgsConstructor
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;

    private final PasswordEncoder passwordEncoder;

    private final SysUserService sysUserService;

    @Override
    @SneakyThrows
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String username = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();

        if (StringUtils.isEmpty(username)) {
            throw new BindException("username", "用户名不能为空");
        }
        if (StringUtils.isEmpty(password)) {
            throw new BindException("password", "密码不能为空");
        }

        SecurityUserDetails userDetails = (SecurityUserDetails) userDetailsService.loadUserByUsername(username);

        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("密码错误");
        }

        if (!userDetails.isAccountNonLocked()) {
            throw new LockedException("用户已锁定, 请联系管理员");
        }

        // 授权
        // -------------------
        // roles | authorities
        // -------------------
        List<Map<String, Object>> rolesPermissions = sysUserService.selectRolesAuthorities(userDetails.getUserId());

        userDetails.setAuthorities(grantedAuthority(rolesPermissions));

        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }

    /**
     * 授权
     *
     * @param rolesPermissions --------------------
     *                         roles | permissions
     *                         --------------------
     *                         角色与权限列表
     * @return
     */
    private List<GrantedAuthority> grantedAuthority(List<Map<String, Object>> rolesPermissions) {
        // 角色与权限
        Set<GrantedAuthority> roles = rolesPermissions.stream()
                .filter(v -> StrUtil.isNotBlank(v.get("role_code").toString()))
                .map(v -> new SimpleGrantedAuthority(v.get("role_code").toString()))
                .collect(Collectors.toSet());

        Set<GrantedAuthority> permissions = rolesPermissions.stream()
                .filter(v -> StrUtil.isNotBlank(v.get("perms").toString()))
                .map(v -> new SimpleGrantedAuthority(v.get("perms").toString()))
                .collect(Collectors.toSet());

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
        grantedAuthorities.addAll(permissions);
        return grantedAuthorities;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}
